package com.ftg.learn.util.tools;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

/**
 * 类型转换通用设计 ，
 * 主要完成，entity与vo的转换
 * 生成树形结构
 * @author kangjx
 * @date 2022/11/19
 */
@SuppressWarnings("all")
public class ConventUtil {

    /**
     * 使用规则 ： 转换的两个类里面的属性一致的才会转换,set方法与get方法同名
     * 静态方法 加载问题 ： 类泛型  是在类实例化的时候调用的
     *    所以我们不能直接在static调用泛型
     * @param targetClass  目标实例，里面一定要有数据
     * @param resultClass  就是一个类型,反射类型
     * @param <ResultClass>  目标类型，当前方法返回值类型
     * @param <TargetClass>  原始数据类型，就是数据里面的某一行的数据
     * @return
     */
    public static  <ResultClass,TargetClass>  ResultClass  conventSwapClass(TargetClass targetClass ,Class<ResultClass> resultClass){
        Object object = null;
        try {
            object = resultClass.newInstance();
            /**
             * getMethods 取到本类当中所有的公共方法和它从父类里面继承的方法
             * getDeclaredMethods 包括公共，保护，默认（包）访问和私有方法，但不包括继承的方法。
             */
            Method[] ms = resultClass.getMethods();

            for (Method m : ms) {
                if(m.getName().startsWith("set")){
                    String targetMethodName = m.getName().replace("set","get");

                    try{
                        Method temp = targetClass.getClass().getMethod(targetMethodName);
                        m.invoke(object,temp.invoke(targetClass));
                    }catch (NoSuchMethodException ex) {
                        continue;
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException  | InvocationTargetException e) {
            e.printStackTrace();
        }
        return (ResultClass)object;
    }


    /**
     * 通用工具 类，用来生成树形结构 的
     * @param id                  主键名称 ，用来声明树的唯一标识，一般为数据库表主键
     * @param pid                 上级节点编号，父节点编号 ，一般为数据库表上级元素  如pid,parent_id ,mgr等
     * @param children            生成树形结构时，相应的节点元素名称
     * @param val                 根节点的数值 一般为 -1 , 0 等
     * @param resultList         resultClass目标类，一般为数据里面的数据
     * @param targetClassClass   targetClassClass 返回类型
     * @param <ResultClass>
     * @param <TargetClass>
     * @return 树
     */
    public static <ResultClass,TargetClass> List<TargetClass> createTree(String id, String pid, String children, Integer val, List<ResultClass> resultList, Class<TargetClass> targetClassClass ){
        //这个就是我们要用到的getPid方法名称
        String pidMethodName = methodNameBySetterGetter(pid,SetGetEnum.GET_TYPE_ENUM);;

        List<TargetClass> list = new ArrayList<>();
        for (ResultClass result : resultList) {
            Integer pidVal = 0;
            try {
                Method tempMethod  = result.getClass().getMethod(pidMethodName);
                Object tempVal = tempMethod.invoke(result);
                pidVal = (Integer) tempVal;
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                System.out.println("pid没有，或者pid参数名称传递错误");
                e.printStackTrace();
            }
            if(pidVal.equals(val)){
                TargetClass vo =ConventUtil.conventSwapClass(result,targetClassClass);
                list.add(vo);
                currentNodeChildren(id,pid,children,resultList,vo);
            }
        }
        return list;
    }

    /**
     *  当前节点currentNode，查找所有子节点，并递归求所有无级节点
     * @param id                  主键名称 ，用来声明树的唯一标识，一般为数据库表主键
     * @param pid                 上级节点编号，父节点编号 ，一般为数据库表上级元素  如pid,parent_id ,mgr等
     * @param children            生成树形结构时，相应的节点元素名称
     * @param resultList         resultClass目标类，一般为数据里面的数据
     * @param currentNode        当前节点
     * @param <ResultClass>
     * @param <TargetClass>
     */
    public static  <ResultClass,TargetClass> void currentNodeChildren(String id,String pid,String children,List<ResultClass> resultList,TargetClass currentNode){

        String pidGetMethodName = methodNameBySetterGetter(pid,SetGetEnum.GET_TYPE_ENUM);
        String idGetMethodName = methodNameBySetterGetter(id,SetGetEnum.GET_TYPE_ENUM);
        String childrenGetMethodName = methodNameBySetterGetter(children,SetGetEnum.GET_TYPE_ENUM);
        String childrenSetMethodName = methodNameBySetterGetter(children,SetGetEnum.SET_TYPE_ENUM);

        for (ResultClass resultClass : resultList) {
            //当前节点的id
            Integer idVal = 0;
            //resultClass 的
            //求出当前currentNode的id
            Integer resultClassPidVal = 0;
            try {
                Method idGetMethod = currentNode.getClass().getMethod(idGetMethodName);
                idVal = (Integer) idGetMethod.invoke(currentNode);

                Method resultClassPidGetMethod = resultClass.getClass().getMethod(pidGetMethodName);
                Object pidObject = resultClassPidGetMethod.invoke(resultClass);;
                resultClassPidVal =  Integer.valueOf(pidObject.toString());

                if(idVal.equals(resultClassPidVal)){
                    Method childrenGetMethod = currentNode.getClass().getMethod(childrenGetMethodName);
                    List<TargetClass> childrenList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
                    if(Objects.isNull(childrenList)){
                        Method childrenSetMethod = currentNode.getClass().getMethod(childrenSetMethodName,List.class);
                        childrenSetMethod.invoke(currentNode,new ArrayList<>(1));
                    }

                    List<TargetClass> childrenSecondList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
                    TargetClass o = (TargetClass) ConventUtil.conventSwapClass(resultClass, currentNode.getClass());
                    childrenSecondList.add(o);
                }


            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }

        Method childrenGetMethod = null;
        try {
            childrenGetMethod = currentNode.getClass().getMethod(childrenGetMethodName);
            List<TargetClass> childrenList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
            if (childrenList == null || childrenList.isEmpty()) {
                return;
            }
            for (TargetClass newSon : childrenList) {
                currentNodeChildren(id,pid,children,resultList,newSon);
            }

        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }


    }

    /**
     * 内部类用来完成对我们setter和getter方式的判断
     */
    private enum SetGetEnum{
        /**
         * set类型
         */
        SET_TYPE_ENUM,
        /**
         * get类型
         */
        GET_TYPE_ENUM
    }

    /**
     * 根据传递过来的属性名称 ，来动态生成相庆的setter与getter方法
     * @param filedName     字段
     * @param typeEnum      类型  SetGetEnum
     * @return
     */
    private static String methodNameBySetterGetter(String filedName,SetGetEnum typeEnum){
        String prefix = "";
        if(typeEnum == SetGetEnum.SET_TYPE_ENUM){
            prefix = "set";
        }

        if(typeEnum == SetGetEnum.GET_TYPE_ENUM){
            prefix = "get";
        }
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(prefix).append(filedName.substring(0,1).toUpperCase(Locale.ROOT)).append(filedName.substring(1));
        return stringBuilder.toString();
    }

    /**
     * 为了解决当前系统当中，固化结构而设计
     * @param resultList         数据里面的数据
     * @param targetClassClass   树的类型
     * @param <ResultClass>     数据里面的数据
     * @param <TargetClass>     树的类型
     * @return
     */
    public static <ResultClass,TargetClass>  List<TargetClass> createTree(List<ResultClass> resultList,Class<TargetClass> targetClassClass ){
        return createTree("id","pid","children",-1,resultList,targetClassClass);
    }


}
